#if !defined ALI_MY_MATH_HEADER_FILE
#define ALI_MY_MATH_HEADER_FILE


#include <iostream.h>
#include <string.h>
#include <stdio.h>
#include "rngs.h"


class CMy_Math_Base
{
	char *classIdentifier;
public:
	CMy_Math_Base::CMy_Math_Base();
	CMy_Math_Base(char *cn);
	virtual ~CMy_Math_Base();
	char * getClassIdentifier();
	void setClassIdentifier(char *ci);
	double getRandomDouble();
	static int myAtoi(char *n);
	static int myPow(int base, int exp);

	virtual void destroyInstance(); // delete current instance dynamic memory
};


class CMy_Matrix:public CMy_Math_Base
{
	int rowCount, colCount;	
public:
	double **mat;

	CMy_Matrix(char *ci);
	CMy_Matrix(int rc, int cc, char *ci);
	~CMy_Matrix();

	int getRowCount();
	int getColCount();

	void randomiseMatrix(); // create random values 
	void randomiseTriDiagonalMatrix(); // create a tridiagonal matrix

	int getDiagonals(CMy_Matrix* &mainDiag, CMy_Matrix* &lowerDiag, CMy_Matrix* &upperDiag);

	void printMatrix(); // print the matrix

	CMy_Matrix* multiply(CMy_Matrix* op2);
	double sumMatrixValues(); // sum all matrix values
	CMy_Matrix* addMatrices(CMy_Matrix* op2);
	CMy_Matrix* createIdentityMatrix(); // crates an identity matix corresponds to the current object

	CMy_Matrix* clone(); // copies the current instance
	
	void destroyInstance();// delete the current instance dynamic data
	
};

/* -----------------------------------------------------------------------------------*/
//									The Math Base class
/* -----------------------------------------------------------------------------------*/

CMy_Math_Base::CMy_Math_Base()
{
	classIdentifier = NULL;
	SelectStream(0);                  /* select the default stream */
	PutSeed(-2);                       /* and set the state to -2    */
}
/////////////////////////////////////////////////////
CMy_Math_Base::CMy_Math_Base(char *ci)
{
	classIdentifier = new char[strlen(ci)];
	strcpy(classIdentifier, ci);
}
/////////////////////////////////////////////////////
CMy_Math_Base::~CMy_Math_Base()
{
	destroyInstance();
}
/////////////////////////////////////////////////////
char * CMy_Math_Base::getClassIdentifier()
{
	return classIdentifier;
}
/////////////////////////////////////////////////////
void CMy_Math_Base::setClassIdentifier(char * ci)
{
	if(classIdentifier != NULL) // delete the old allocated memory
		delete classIdentifier;
	classIdentifier = new char[strlen(ci)];
	strcpy(classIdentifier, ci);
}
/////////////////////////////////////////////////////
// generate a random double
double CMy_Math_Base::getRandomDouble()
{	
	double r = Random();
	while(r<1.0)
		r *= 10;
	return r;
}
/////////////////////////////////////////////////////
// return the base ^ exp for +ve base and exp
int CMy_Math_Base::myPow(int base, int exp)
{
	int res = 1;
	for(int i=0; i<exp; i++)
		res *= base;
	return res;
}
/////////////////////////////////////////////////////
// Convert a string to integer
int CMy_Math_Base::myAtoi(char *n)
{
	int res = 0, size = strlen(n);
	for(int i=0; i<size; i++)
	{
		//cout << "\n digit " << (int)n[i]-(int)'0' << " Pos = " << i << " power = " << myPow(10, i);
		res += ((int)n[i]-(int)'0') * myPow(10, size - 1 - i);
	}
	return res;
}
/////////////////////////////////////////////////////
// Destroy current instance dynamic data
void CMy_Math_Base::destroyInstance()
{	
	delete[] classIdentifier;
}
/* -----------------------------------------------------------------------------------*/
//									The Matrix class
/* -----------------------------------------------------------------------------------*/

CMy_Matrix::CMy_Matrix(char *mn): CMy_Math_Base(mn)
{
	rowCount = 0;
	colCount = 0;
	mat = 0;
}
/////////////////////////////////////////////////////
CMy_Matrix::CMy_Matrix(int rc, int cc, char *mn) : CMy_Math_Base(mn)
{
	rowCount = rc;
	colCount = cc;
	mat = new double* [rowCount];
	for(int i=0; i<rowCount; i++)
		mat[i] = new double[colCount];
}
/////////////////////////////////////////////////////
CMy_Matrix::~CMy_Matrix()
{
	this->destroyInstance();
}
/////////////////////////////////////////////////////
int CMy_Matrix::getRowCount()
{
	return rowCount;
}
/////////////////////////////////////////////////////
int CMy_Matrix::getColCount()
{
	return colCount;
}
/////////////////////////////////////////////////////
void CMy_Matrix::randomiseMatrix()
{
	// For testing; all values are randomised
	//double r;
	//srand ( time(NULL) );
	for(int i=0; i<rowCount; i++)	
		for(int j=0; j<colCount; j++)
		{			
			/*r = (   (double)rand() / ((double)(RAND_MAX)+(double)(1)) );
			mat[i][j] =  (rand() % 20) * r;
			if( r < 0.5 ) // make it -ve number
				mat[i][j] *=  -1;			
				*/
			mat[i][j] = getRandomDouble();
		}
}
/////////////////////////////////////////////////////
void CMy_Matrix::randomiseTriDiagonalMatrix() // create a tridiagonal matrix
{	
	int i;
		
	// fill the diagonals	
	for(i=0; i<rowCount; i++)
	{
		// just put 0 in all row entries
		memset(mat[i], 0, colCount*sizeof(double));
		//cout << "\n Row (" << i << ") is cleared" << flush;

		// main diagonal
		mat[i][i] = getRandomDouble();
		//cout << "\nMD[" << i <<"][" << j << "] = " << mat[i][j];

		// lower diagonal		
		if(i<rowCount-1)
		{
			mat[i+1][i] = getRandomDouble();
			//cout << "\nLD[" << i+1 <<"][" << i << "] = " << mat[i+1][i];
		}

		// uper diagonal		
		if(i<colCount-1)
		{
			mat[i][i+1] = getRandomDouble();
			//cout << "\nUD[" << i <<"][" << i+1 << "] = " << mat[i][i+1];
		}

		
	}	
}
/////////////////////////////////////////////////////
void CMy_Matrix::printMatrix()
{
	cout << "\n ======== Printing out the Matrix (" <<getClassIdentifier()
		 << ") Size(rows, columns) = " << rowCount << ", " << colCount << "========\n"<<flush;
	
	for(int i=0; i<rowCount; i++)
	{		
		for(int j=0; j<colCount; j++)
			//cout << "\nmat[" << i <<"][" << j << "] = " << mat[i][j];
			//cout << mat[i][j] << "\t";
			cout << mat[i][j] << "\n";
		
		//cout << "\n";
	}	
	cout << flush;
}
/////////////////////////////////////////////////////
// do multiplication and store the result in a new matrix
CMy_Matrix* CMy_Matrix::multiply(CMy_Matrix* op2)
{
	if(colCount != op2->rowCount)
	{
		cout << "\n Can not do multiplication (Op1-Col != Op2-Row) ";
		return NULL;
	}
	
	CMy_Matrix *result = new CMy_Matrix(rowCount, op2->colCount, "Multiplication Result");	

	for(int i=0; i<rowCount; i++)
	{		
		for(int j=0; j<op2->colCount; j++)
		{			
			result->mat[i][j] = 0.0;

			for(int k=0; k<colCount; k++)
				result->mat[i][j] += mat[i][k] * op2->mat[k][j];
		}
	}
	return result;
}
/////////////////////////////////////////////////////
double CMy_Matrix::sumMatrixValues()
{
	double res = 0.0;
	for(int i=0; i<rowCount; i++)
		for(int j=0; j<colCount; j++)
			res += mat[i][j];
	return res;
}
/////////////////////////////////////////////////////
// Creates an identity matix corresponds to the current object
CMy_Matrix* CMy_Matrix::createIdentityMatrix()
{
	CMy_Matrix* identity = new CMy_Matrix(colCount, colCount, "Identity Matrix");

	for(int i=0; i<colCount; i++)
		for(int j=0; j<colCount; j++)
			if(i==j)
				identity->mat[i][i] = 1.0;
			else
				identity->mat[i][i] = 0.0;	
	return identity;
}
/////////////////////////////////////////////////////
// Add two matrices
CMy_Matrix* CMy_Matrix::addMatrices(CMy_Matrix* op2)
{
	if( (rowCount!= op2->rowCount) || (colCount != op2->colCount))
		return NULL;

	CMy_Matrix* res = new CMy_Matrix(rowCount, colCount, "Matrix Summation");

	for(int i=0; i<rowCount; i++)
		for(int j=0; j<colCount; j++)					
			res->mat[i][j] = mat[i][j] + op2->mat[i][j];
		
	return res;
}
/////////////////////////////////////////////////////
/* assumption:
the matrix is a tridiagonal matrix
*/
int CMy_Matrix::getDiagonals(CMy_Matrix* &mainDiag, CMy_Matrix* &lowerDiag, CMy_Matrix* &upperDiag)
{
	if(rowCount != colCount)
		return 0; // it has to be tridiagonal matix first

	mainDiag  = new CMy_Matrix(1, getColCount(), "Main Diagonal");
	lowerDiag = new CMy_Matrix(1, getColCount()-1, "Lower Diagonal");
	upperDiag = new CMy_Matrix(1, getColCount()-1, "Upper Diagonal");
	
	for(int i=0; i<rowCount; i++)
	{
		// main diagonal
		mainDiag->mat[0][i] = mat[i][i];
		// lower diagonal
		if(i<rowCount-1)
			lowerDiag->mat[0][i] = mat[i+1][i];
		// uper diagonal
		if(i<colCount-1)
			upperDiag->mat[0][i] = mat[i][i+1];
	}


	return 1;
}
/////////////////////////////////////////////////////
// copy the current instance into a new one
CMy_Matrix* CMy_Matrix::clone()
{
	CMy_Matrix* newInstance = new CMy_Matrix(rowCount, colCount, getClassIdentifier());
	for(int i=0; i<rowCount; i++)
		memcpy(newInstance->mat[i], mat[i], sizeof(double)*colCount);
	return newInstance;
}
/////////////////////////////////////////////////////
// delete the current instance dynamic data
void CMy_Matrix::destroyInstance()
{
	//CMy_Math_Base::destroyInstance();
	//cout << "\n Hello" << flush;
	for(int i=0; i<rowCount; i++)
		delete[] mat[i];
	delete [] mat;	
	//cout << "\n Bye" << flush;
}
/////////////////////////////////////////////////////
#endif // !defined ALI_MY_MATH_HEADER_FILE